package com.eairlv.route.agent.filter;

import com.alibaba.fastjson.JSON;
import com.eairlv.route.agent.entity.Result;
import com.eairlv.route.agent.service.AuthenticationProperties;
import com.eairlv.route.agent.service.AuthenticationService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import java.util.Enumeration;
import java.util.Optional;

/**
 * @author eairlv
 * @description
 * @date 10:01 2019/8/28
 */
@Slf4j
@Component
public class RouteAccessFilter extends ZuulFilter {

    private static final String ROUTE_HEADER = "ROUTE_HEADER";

    private static AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    AuthenticationProperties authenticationProperties;

    @Autowired
    AuthenticationService authenticationService;

    /**
     * 屏蔽内部请求头
     * 如果不屏蔽，服务端接口无法读取MultipartFile类型参数
     */
    public static final String CONTENT_TYPE = "Content-Type";

    public static final String CONTENT_LENGTH = "Content-Length";

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        try {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            // 借助HttpServletRequestWrapper重置request,解决inputStream只能读一次问题
            HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request);
            ctx.setRequest(requestWrapper.getRequest());

            // OPTIONS请求不进行路由
            if (HttpMethod.OPTIONS.equals(request.getMethod())) {
                ctx.setSendZuulResponse(false);
                return null;
            }

            // 接口权限拦截
            if (!authentication(request.getHeader(ROUTE_HEADER), request.getRequestURI())) {
                ctx.setResponseBody(JSON.toJSONString(Result.failMessage("invalid token")));
                ctx.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
                ctx.setSendZuulResponse(false);
                return null;
            }

            // 传递请求头
            header(request, ctx);

//            // 模拟成功
//            ctx.setResponseBody(JSON.toJSONString(Result.ok()));
//            ctx.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
//            ctx.setSendZuulResponse(false);

            return null;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("ERROR {}", e.toString());
        }
        return null;
    }

    /**
     * 接口鉴权
     * @param token
     * @param requestURI
     * @return
     */
    private Boolean authentication(String token, String requestURI){
        if (authenticationProperties.getIgnore() == null){
            return token != null && authenticationService.checkToken(token);
        } else {
            Optional<String> match = authenticationProperties.getIgnore()
                    .parallelStream().filter(e -> antPathMatcher.match(e, requestURI)).findAny();
            if (match.isPresent()){
                return Boolean.TRUE;
            } else {
                return token != null && authenticationService.checkToken(token);
            }
        }
    }


    /**
     * 传递请求头
     * @param request
     * @param ctx
     */
    private void header(HttpServletRequest request, RequestContext ctx) {
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()){
            String name = headerNames.nextElement();
            if (!name.equalsIgnoreCase(CONTENT_TYPE) && !name.equalsIgnoreCase(CONTENT_LENGTH)){
                ctx.addZuulRequestHeader(name, request.getHeader(name));
            }
        }
    }
}
